'''
Created on Feb 25, 2012

@author: daoxuandung
'''

"""
authenticate module provide handlers for registration, 
login, logout, confirm email, reset password

It should support Facebook using OAuth protocol
Google and Yahoo login using OpenID

User will login using email address. User can login by username and password
or authorized by third party

An account can have only one auth_id. User can add Facebook, Google
information to this account, but can only use one credential to login
"""

from google.appengine.ext import ndb
from google.appengine.api import users
from google.appengine.ext import deferred
from webapp2 import uri_for
from webapp2_extras.appengine.auth import models
from webapp2_extras import security
from webapp2_extras import auth
import base
import urllib
from httplib2 import Http
from urlparse import parse_qsl
import json, logging
import random
import urls
import util
import settings
from external import fb

class RegisterHandler(base.BaseHandler):
    """
    User can create new account by email address and password
    """
    def get(self):
        template = util.jinja_environment().get_template('create-user.html')
        template_values = {
                           'register_url':uri_for('register'),
                           'fb_login_url':uri_for('facebook-login')
                           }
        self.response.out.write(template.render(template_values))
    
    def post(self):
        # 1) Get email address and password and after-register URL
        email = self.request.POST['email']
        password = self.request.POST['password']
            
        # 2) Check if email address existed
        existing_user = models.User.get_by_auth_id(email)
        #    If not, create new account and redirect to after-register URL
        if (existing_user == None):
            created, user = models.User.create_user(email, password_raw=password)
            # Create auth_token and send email confirm
            if created:
                user_id = user.key.id()
            token = models.User.create_signup_token(user_id)
            # Login user automatically
            models.User.get_by_auth_password(email, password)
            confirm_email_url = uri_for('confirm-email')
            confirm_email_url += "?token=" + token + "&user-id=" + str(user_id)
            template = util.jinja_environment().get_template('register-result.html')
            template_values = {'confirm_email_url':confirm_email_url}
            self.response.out.write(template.render(template_values))            
        else:
            self.response.write("User already existed")


class FacebookHandler(base.BaseHandler):
    """
    Follow closely with Facebook API, include three legs
    1) Request for user permission
    2) Authenticate app to receive access_token
    3) Retrieve user's data using received token
    
    Using TaskQueue to process following friends in parallel
    """
    @ndb.synctasklet
    def get(self):
        code = self.request.params.get('code')
        if (code == None):
            # Step 1: Request for permission
            request_token_url = "https://www.facebook.com/dialog/oauth"\
                        "?client_id=%s&redirect_uri=%s" \
                        "&scope=email" \
                        %(settings.FACEBOOK_APP_ID,
                          urllib.quote(uri_for('facebook-login', _full=True)))
            self.redirect(request_token_url)
        
        else:
            # Step 2: Authenticate app
            access_token_url = "https://graph.facebook.com/oauth/access_token" \
                               "?client_id=%s" \
                               "&redirect_uri=%s" \
                               "&client_secret=%s"\
                               "&code=%s" \
                               %(settings.FACEBOOK_APP_ID,
                                 urllib.quote(uri_for('facebook-login', _full=True)),
                                 settings.FACEBOOK_APP_SECRET,
                                 code)
            http = Http()
            resp, content = http.request(access_token_url)
            if (resp.status == 200):
                # Step 3: Retrieve current user
                access_token = dict(parse_qsl(content))['access_token']
                logging.info(access_token)
                existing_user = yield fb.me(access_token)
                    
                # Login
                self.login(existing_user, remember=True)                
                self.redirect(uri_for('home-page'))
                                

class OpenIDHandler(base.BaseHandler):
    def get(self, provider):
        redirect_url = uri_for('openid-result', provider=provider)
        if (provider == "google"):
            self.redirect(users.create_login_url(redirect_url))
        elif (provider == "yahoo"):
            self.redirect(users.create_login_url(redirect_url, "yahoo", "yahoo.com"))
        else:
            self.response.write("OpenID provider not supported")

class OpenIDResultHandler(base.BaseHandler):
    def get(self, provider):
        user = users.get_current_user()
        if user is None:
            return
        # Create a new user if not found
        email = user.email()
        existing_user = models.User.get_by_auth_id(email)
        password_raw = str(random.randint(1000,100000))
        if (existing_user is None):
            rv = models.User.create_user(email,
                                    password_raw=password_raw)
            existing_user = rv[1]
            
        # Login
        self.login(existing_user, remember=True)
        self.redirect(uri_for('home-page'))
    
class LoginHandler(base.BaseHandler):
    def get(self):
        try:
            #query_dict = dict(self.request.path_qs)
            redirect_url = self.request.GET['redirect-url']
        except:
            redirect_url = uri_for('home-page')
        
        if (util.get_current_user_key() == None):
            template = util.jinja_environment().get_template('login.html')
            template_values = {
                               'login_url':uri_for('login'),
                               'redirect_url':redirect_url,
                               'fb_login_url':uri_for('facebook-login'),
                               'google_login_url':uri_for('openid', provider='google'),
                               'yahoo_login_url':uri_for('openid', provider='yahoo'),
                               'reset_password_url':uri_for('reset-password')
                               }
            self.response.out.write(template.render(template_values))
            
        else:
            self.redirect(redirect_url)
    
    def post(self):
        email = self.request.POST['email']
        password = self.request.POST['password']
        try:
            redirect_url = self.request.POST['redirect-url']
        except:
            redirect_url = uri_for('event-page')
            
        try:
            if (self.request.POST['remember-me']):
                remember = True
        except:
            remember = False
            
        try:
            auth.get_auth().get_user_by_password(email, 
                                              password, 
                                              remember=remember)

            self.redirect(redirect_url)
        except auth.InvalidAuthIdError: # User not existed
            self.response.write("User not existed")
        except auth.InvalidPasswordError: # Wrong password
            self.response.write("Wrong password")
            
        
        
        
class LogoutHandler(base.BaseHandler):
    def get(self):
        try:
            redirect_url = self.request.GET['redirect-url']
        except:
            redirect_url = uri_for('login')
        
        auth.get_auth().unset_session()
        self.redirect(redirect_url)
        
    
class ConfirmEmailHandler(base.BaseHandler):
    def get(self):
        try:
            token = self.request.params['token']
            user_id = self.request.params['user-id']
        except:
            token = ""
            user_id = ""
        
        if models.User.validate_signup_token(user_id, token):        
            self.response.write("Confirm email successful")
        else:
            self.response.write("Unable to verify email address")

    
class ResetPasswordHandler(base.BaseHandler):
    def get(self):
        template = util.jinja_environment().get_template('reset-password.html')
        template_values = {
                           'reset_password_url':uri_for('reset-password')
                           }
        self.response.out.write(template.render(template_values))

    def post(self):
        try:
            email = self.request.POST['email']
        except:
            pass
        
        existing_user = models.User.get_by_auth_id(email)
        if (existing_user == None):
            self.response.write("No user found")
        else:
            token = models.User.token_model.create(existing_user, 
                                                   'reset-password').token
            reset_password_link = uri_for('reset-password-complete')
            reset_password_link += "?token=" + token + "&email=" + email
            template = util.jinja_environment().get_template('reset-password-confirm.html')
            template_values = {
                               'reset_password_link':reset_password_link,
                               'email':email
                               }
            self.response.out.write(template.render(template_values))


class ResetPasswordCompleteHandler(base.BaseHandler):
    def verify_token(self):
        try:
            token = self.request.params['token']
        except:
            token = ""
        
        test = (models.User.token_model.token == token)
        existing_token = models.User.token_model.query(test).get()
        return existing_token
        
    def get(self):
        existing_token = self.verify_token()
        try:
            email = self.request.GET['email']
        except:
            email = ""
            
        if existing_token:
            template = util.jinja_environment().get_template('reset-password-complete.html')
            template_values = {
                               'token':existing_token,
                               'email':email
                               }
            self.response.out.write(template.render(template_values))
        
    def post(self):
        existing_token = self.verify_token()
        try:
            email = self.request.POST['email']
        except:
            pass
        
        user = models.User.get_by_auth_id(email)

        if existing_token and existing_token.user==str(user):
            user.password = security.generate_password_hash(self.request.params['password'], 
                                                            length=12)
            user.put_async()
            # Delete token
            existing_token.key.delete_async()
            # Login user
            self.auth.get_user_by_password(user.auth_ids[0], self.request.params['password'])
            self.response.write("Change password successful")

app = urls.app

def main():
    app.run()

if __name__ == '__main__':
    main()